package com.rao.gmall.cart.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rao.gmall.cart.service.CartService;
import com.rao.gmall.common.constant.RedisConst;
import com.rao.gmall.feign.product.SkuInfoFeignClient;
import com.rao.gmall.model.cart.CartInfo;
import com.rao.gmall.model.product.SkuInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author nzmxfan
 * @create 2021-08-17-19:41
 */
@Service
public class CartServiceImpl implements CartService {

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    SkuInfoFeignClient skuInfoFeignClient;

    /**
     *
     * 判断用户是否登录，然后使用对应的购物车
     *
     * 1、用户登录了。
     *      网关会带 userId 头、userTempId 头
     * 2、用户没登录。
     *      网关会只带 userTempId 头
     *
     * @param request
     * @return
     */
    @Override
    public String determinCartKey(HttpServletRequest request) {

        String userId = request.getHeader("userId");
        if (!StringUtils.isEmpty(userId)){
            // 网关透传了 id 数据
            return RedisConst.CART_KEY_PREFIX + userId;
        }else {
            // 用户未登录，使用临时购物车
            String userTempId = request.getHeader("userTempId");
            return RedisConst.CART_TEMP_PREFIX + userTempId;
        }

    }

    /**
     * 向购物车中保存数据
     *
     * @param key
     * @param skuId
     * @param skuNum
     * @return
     */
    @Override
    public CartInfo addToCart(String key, Long skuId, Integer skuNum) {

        CartInfo result = null;
        /**
         *  redis 中存错的结果 key Map< key : value >
         *
         * key： key是 redis 中的 key
         * skuId： redis 中 value 的 key
         */
        // 根据 key 获取对应的 map 类型的值
        BoundHashOperations<String, String, String> hashOps = getCart(key);
        // 用户购物车和临时购物车过期时间不一样
        if (hashOps.hasKey(skuId.toString())){
            // redis 中有这个商品，数量叠加
            // 先获取这个商品信息
            String jsonStr = hashOps.get(skuId.toString());
            // 反序列化，将 json 转为 java 对象
            try {
                result = new ObjectMapper().readValue(jsonStr, CartInfo.class);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }

            Integer num = result.getSkuNum();
            // 修改购物车对象
            result.setSkuNum(num + skuNum);
            // 再次查询价格
            BigDecimal skuPrice = skuInfoFeignClient.getSkuPrice(skuId);
            result.setSkuPrice(skuPrice);

            // 序列化（保存到 redis 中需要序列化）
            String string = null;
            try {
                string = new ObjectMapper().writeValueAsString(result);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            hashOps.put(skuId.toString(), string);
        }else {
            // 给购物车保存不存在的商品信息
            // 远程获取该商品的详细信息
            SkuInfo skuInfo = skuInfoFeignClient.getSkuInfo(skuId);
            // 将 skuInfo 对象转换为 cartInfo 对象
            result = CartInfo.skuInfoToCartInfo(skuInfo);
            // 获取价格
            BigDecimal skuPrice = skuInfoFeignClient.getSkuPrice(skuId);
            result.setSkuPrice(skuPrice);
            // 设置数量
            result.setSkuNum(skuNum);

            // 序列化
            String jsonStr = null;
            try {
                jsonStr = new ObjectMapper().writeValueAsString(result);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            hashOps.put(skuId.toString(), jsonStr);
            if (key.startsWith("cart:temp:")){
                // 临时购物车需要设置过期时间
                redisTemplate.expire(key, RedisConst.TEMP_CART_EXPIRE, TimeUnit.SECONDS);
            }
        }
        return result;
    }

    @Override
    public String tempCartKey(HttpServletRequest request) {
        String userTempId = request.getHeader("userTempId");
        if (!StringUtils.isEmpty(userTempId)){
            return RedisConst.CART_TEMP_PREFIX + userTempId;
        }
        return null;
    }

    @Override
    public String userCartKey(HttpServletRequest request) {
        String userId = request.getHeader("userId");
        if (!StringUtils.isEmpty(userId)){
            return RedisConst.CART_KEY_PREFIX + userId;
        }
        return null;
    }

    @Override
    public List<CartInfo> listCart(String userCartKey, String tempCartKey) {
        List<CartInfo> result = null;
        /**
         * 1、如果用户登录了。key；默认是登录用户。购物车是用户购车。
         * 但是还带了 userTempId。代表没登录前用的临时车
         * 2、判断如果用户登录了，带了 userTempId，购物车需要合并
         */
        if (!StringUtils.isEmpty(tempCartKey)){
            BoundHashOperations<String, String, String> cart = getCart(tempCartKey);
            if (cart.size() > 0){
                // 临时购物车有数据，获取所有数据
                result = getCartInfoList(tempCartKey);
            }
        }
        if (!StringUtils.isEmpty(userCartKey)){
            // 用户登陆了，需要把临时购物车数据合并到用户购物车中
            if (!CollectionUtils.isEmpty(result)){
                // 合并操作
                for (CartInfo cartInfo : result) {
                    // 将临时购物车商品添加到用户购物车
                    addToCart(userCartKey, cartInfo.getSkuId(), cartInfo.getSkuNum());
                    // 清空临时购物车
                    clearCart(tempCartKey);
                }
            }
            result = getCartInfoList(userCartKey);
        }
        return result;
    }

    @Override
    public void ajaxCheckCart(String key, Long skuId, Integer checked) {
        // 获取购物车
        BoundHashOperations<String, String, String> cart = getCart(key);
        // 根据 key 获取对应的值
        String jsonStr = cart.get(skuId.toString());
        //用来接收反序列化的对象
        CartInfo cartInfo = null;
        try {
        // 反序列化
            cartInfo = new ObjectMapper().readValue(jsonStr, CartInfo.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        // 设置勾选状态
        cartInfo.setIsChecked(checked);

        String cartInfoStr = null;
        try {
        // 将数据序列化为 json 串
            cartInfoStr = new ObjectMapper().writeValueAsString(cartInfo);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        // 判断商品项是否超标（应该在所有添加数据地方都进行判断）
        if (cart.size() > 100){
            throw new RuntimeException("购物车商品数量超过限制");
        }
        // 将序列化的数据存入 redis
        cart.put(skuId.toString(), cartInfoStr);

    }

    @Override
    public void deleteCartInfo(String key, Long skuId) {
        // 获取购物车
        BoundHashOperations<String, String, String> cart = getCart(key);
        // 删除购物项
        cart.delete(skuId.toString());
    }

    /**
     * 获取所有被选中的商品信息
     * @param key
     * @return
     */
    @Override
    public List<CartInfo> listCheckCartInfo(String key) {
        // 创建一个集合，接收所有被选中的商品
        ArrayList<CartInfo> cartInfos = new ArrayList<>();
        // 根据 key 获取对应的购物车
        BoundHashOperations<String, String, String> cart = getCart(key);

        ObjectMapper mapper = new ObjectMapper();
        // 再根据购物车获取所有商品
        List<String> values = cart.values();
        if (!CollectionUtils.isEmpty(values)){
            for (String value : values) {
                // 根据 key 获取对应的值
                CartInfo cartInfo = null;
                try {
                    // 反序列化
                    cartInfo = mapper.readValue(value, CartInfo.class);
                        // 该商品为被选中商品
                        // 将商品创建时间和更新时间设为 null，便于自动设置创建/更新订单的时间
                        cartInfo.setCreateTime(null);
                        cartInfo.setUpdateTime(null);

                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
                if (cartInfo.getIsChecked().intValue() == 1){
                    cartInfos.add(cartInfo);
                }
            }
        }

        return cartInfos;
    }

    @Override
    public void deleteBatch(String key, List<String> skuIds) {
        // 获取购物车
        BoundHashOperations<String, String, String> cart = getCart(key);
        // 购物车的 string[]
        String[] strings = skuIds.toArray(new String[skuIds.size()]);

        // 批量删除指定的购物项
        cart.delete(strings);

    }

    private void clearCart(String tempCartKey) {
        // 删除购物车（删除 redis 中对应的 key）
        redisTemplate.delete(tempCartKey);
    }

    /**
     * 获取购物车所有商品
     * @param key
     * @return
     */
    private List<CartInfo> getCartInfoList(String key) {
        // 准备一个集合，用来存放数据
        List<CartInfo> cartInfos = new ArrayList<>();
        // 拿到购物车
        BoundHashOperations<String, String, String> cart = getCart(key);
        // 拿到购物车存储的所有的 key    Map<key1 : value1, key2 : value2...>
        List<String> values = cart.values();
        if (!CollectionUtils.isEmpty(values)){
            // 这个购物车在 redis 中存在数据
            for (String skuJson : values) {
                // 获取购物项，并进行反序列化转化为 cartInfo 对象
                CartInfo cartInfo = null;
                try {
                    cartInfo = new ObjectMapper().readValue(skuJson, CartInfo.class);
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
                cartInfos.add(cartInfo);
            }
        }
        return cartInfos;
    }


    /**
     * 根据 key 获取对应的 map 类型的值
     *          ----------------> key：map<key：value>
     * @param key
     * @return
     */
    private BoundHashOperations<String, String, String> getCart(String key) {
        return redisTemplate.boundHashOps(key);
    }
}
